home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / aptdaemon / client.py < prev    next >
Text File  |  2009-10-14  |  21KB  |  513 lines

  1. #!/usr/bin/python
  2. """
  3. The module provides a client to the PackageKit DBus interface. It allows to
  4. perform basic package manipulation tasks in a cross distribution way, e.g.
  5. to search for packages, install packages or codecs.
  6. """
  7. # Copyright (C) 2008 Canonical Ltd.
  8. # Copyright (C) 2008 Aidan Skinner <aidan@skinner.me.uk>
  9. # Copyright (C) 2008 Martin Pitt <martin.pitt@ubuntu.com>
  10. # Copyright (C) 2008 Tim Lauridsen <timlau@fedoraproject.org>
  11. # Copyright (C) 2008-2009 Sebastian Heinlein <devel@glatzor.de>
  12. #
  13. # Licensed under the GNU General Public License Version 2
  14. #
  15. # This program is free software; you can redistribute it and/or modify
  16. # it under the terms of the GNU General Public License as published by
  17. # the Free Software Foundation; either version 2 of the License, or
  18. # (at your option) any later version.
  19. #
  20. # This program is distributed in the hope that it will be useful,
  21. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23. # GNU General Public License for more details.
  24. #
  25. # You should have received a copy of the GNU General Public License
  26. # along with this program; if not, write to the Free Software
  27. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  28.  
  29. import locale
  30. import os
  31. import weakref
  32.  
  33. import dbus
  34. import dbus.glib
  35. import dbus.mainloop.glib
  36. dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
  37. import gobject
  38.  
  39. import enums
  40. import debconf
  41. import errors
  42.  
  43. class AptMessage:
  44.     """Represents a non-cirtical information or warning from the daemon"""
  45.     def __init__(self, enum, details):
  46.         self.enum = enum
  47.         self.details = details
  48.  
  49. class AptDaemonError(Exception):
  50.     """Wrapper for a ErrorCode message"""
  51.     def __init__(self, code, details=None):
  52.         Exception.__init__(self)
  53.         self.code = code
  54.         self.details = details
  55.  
  56.     def __str__(self):
  57.         return "%s: %s" % (self.code, self.details)
  58.  
  59.  
  60. class MemoizedTransaction(type):
  61.  
  62.     """Metaclass to cache transactions."""
  63.  
  64.     cache = weakref.WeakValueDictionary()
  65.  
  66.     def __call__(cls, *args):
  67.         tid = args[0]
  68.         try:
  69.             return cls.cache[tid]
  70.         except KeyError:
  71.             cls.cache[tid] = value = \
  72.                     super(MemoizedTransaction, cls).__call__(*args)
  73.             return value
  74.  
  75.  
  76. class MemoizedMixIn(MemoizedTransaction, gobject.GObjectMeta):
  77.     pass
  78.  
  79.  
  80. class AptTransaction(gobject.GObject):
  81.  
  82.     """Represents an aptdaemon transaction. It allows asynchronous and
  83.     synchronous processing.
  84.     """
  85.  
  86.     __metaclass__ = MemoizedMixIn
  87.  
  88.     __gsignals__ = {"finished" : (gobject.SIGNAL_RUN_FIRST,
  89.                                   gobject.TYPE_NONE,
  90.                                   (gobject.TYPE_INT,)),
  91.                     "error" : (gobject.SIGNAL_RUN_FIRST,
  92.                                gobject.TYPE_NONE,
  93.                                (gobject.TYPE_INT, gobject.TYPE_STRING)),
  94.                     "role" : (gobject.SIGNAL_RUN_FIRST,
  95.                               gobject.TYPE_NONE,
  96.                               (gobject.TYPE_INT,)),
  97.                      "allow-terminal" : (gobject.SIGNAL_RUN_FIRST,
  98.                                         gobject.TYPE_NONE,
  99.                                         (gobject.TYPE_BOOLEAN,)),
  100.                     "allow-cancel" : (gobject.SIGNAL_RUN_FIRST,
  101.                                       gobject.TYPE_NONE,
  102.                                       (gobject.TYPE_BOOLEAN,)),
  103.                     "status" : (gobject.SIGNAL_RUN_FIRST,
  104.                                 gobject.TYPE_NONE,
  105.                                 (gobject.TYPE_INT,)),
  106.                     "status-details" : (gobject.SIGNAL_RUN_FIRST,
  107.                                         gobject.TYPE_NONE,
  108.                                         (gobject.TYPE_STRING,)),
  109.                     "progress" : (gobject.SIGNAL_RUN_FIRST,
  110.                                   gobject.TYPE_NONE,
  111.                                   (gobject.TYPE_INT,)),
  112.                     "progress-details" : (gobject.SIGNAL_RUN_FIRST,
  113.                                           gobject.TYPE_NONE,
  114.                                           (gobject.TYPE_INT, gobject.TYPE_INT,
  115.                                            gobject.TYPE_INT, gobject.TYPE_INT,
  116.                                            gobject.TYPE_INT, gobject.TYPE_INT)),
  117.                     "medium-required" : (gobject.SIGNAL_RUN_FIRST,
  118.                                          gobject.TYPE_NONE,
  119.                                          (gobject.TYPE_STRING,
  120.                                           gobject.TYPE_STRING)),
  121.                     "config-file-prompt" : (gobject.SIGNAL_RUN_FIRST,
  122.                                             gobject.TYPE_NONE,
  123.                                             (gobject.TYPE_STRING,
  124.                                              gobject.TYPE_STRING)),
  125.                     }
  126.  
  127.     def __init__(self, tid, bus=None):
  128.         gobject.GObject.__init__(self)
  129.         self.tid = tid
  130.         self._role = enums.ROLE_UNSET
  131.         self._error_code = None
  132.         self._error_details = None
  133.         self._exit = None
  134.         self._allow_cancel = False
  135.         self._allow_terminal = False
  136.         self._required_medium = None
  137.         self._config_file_prompt = None
  138.         self._status = None
  139.         self._status_details = ""
  140.         self._progress = 0
  141.         self._method = None
  142.         self._exit_handler = None
  143.         self._messages = []
  144.         self._method = None
  145.         self._args = []
  146.         self._debconf_helper = None
  147.         # Connect the signal handlers to the DBus iface
  148.         if not bus:
  149.             bus = dbus.SystemBus()
  150.         self._proxy = bus.get_object("org.debian.apt", tid)
  151.         self._iface = dbus.Interface(self._proxy, "org.debian.apt.transaction")
  152.         # Watch for a crashed daemon which orphaned the dbus object
  153.         self._owner_watcher = bus.watch_name_owner("org.debian.apt",
  154.                                                    self._on_name_owner_changed)
  155.         # main signals
  156.         self._signal_matchers = []
  157.         for sig, cb in [('Finished', self._on_finished),
  158.                         ('ErrorCode', self._on_error),
  159.                         ('Message', self._on_message),
  160.                         ('Role', self._on_role),
  161.                         ('Status', self._on_status),
  162.                         ('StatusDetails', self._on_status_details),
  163.                         ('Progress', self._on_progress),
  164.                         ('ProgressDetails', self._on_progress_details),
  165.                         ('MediumRequired', self._on_medium_required),
  166.                         ('AllowTerminal', self._on_allow_terminal),
  167.                         ('ConfigFilePrompt', self._on_config_file_prompt),
  168.                         ('AllowCancel', self._on_allow_cancel)]:
  169.             matcher = self._iface.connect_to_signal(sig, cb, utf8_strings=True)
  170.             self._signal_matchers.append(matcher)
  171.         self._main_loop = gobject.MainLoop()
  172.  
  173.     def _on_name_owner_changed(self, connection):
  174.         """Fail the transaction if the daemon died."""
  175.         if connection == "" and not self._exit:
  176.             self._on_error(enums.ERROR_DAEMON_DIED,
  177.                            "It seems that the daemon died.")
  178.             self._on_allow_cancel(False)
  179.             self._on_allow_terminal(False)
  180.             self._on_finished(enums.EXIT_FAILED)
  181.  
  182.     def _on_allow_terminal(self, allow_terminal):
  183.         """Callback for AllowTerminal signal"""
  184.         self._allow_terminal = allow_terminal
  185.         self.emit("allow-terminal", allow_terminal)
  186.  
  187.     def _on_allow_cancel(self, allow_cancel):
  188.         """Callback for AllowCancel signal"""
  189.         self._allow_cancel = allow_cancel
  190.         self.emit("allow-cancel", allow_cancel)
  191.  
  192.     def _on_role(self, role):
  193.         """Callback for Role signal"""
  194.         self._role = role
  195.         self.emit("role", role)
  196.  
  197.     def _on_status(self, status):
  198.         """Callback for Status signal"""
  199.         self._status = status
  200.         self.emit("status", status)
  201.  
  202.     def _on_status_details(self, details):
  203.         """Callback for StatusDetails signal"""
  204.         self._status_details = details
  205.         self.emit("status-details", details)
  206.  
  207.     def _on_progress(self, percent):
  208.         """Callback for Progress signal"""
  209.         self._progress = percent
  210.         self.emit("progress", percent)
  211.  
  212.     def _on_config_file_prompt(self, old, new):
  213.         """Callback for ConfigFilePrompt signal"""
  214.         self._config_file_prompt = (old, new)
  215.         self.emit("config-file-prompt", old, new)
  216.  
  217.     def _on_medium_required(self, label, drive):
  218.         """Callback for MediumRequired signal"""
  219.         self._required_medium = (label, drive)
  220.         self.emit("medium-required", label, drive)
  221.  
  222.     def _on_progress_details(self, items_done, items_total, bytes_done,
  223.                              bytes_total, speed, eta):
  224.         """Callback for ProgressDetails signal"""
  225.         self.emit("progress-details", items_done, items_total, bytes_done,
  226.                   bytes_total, speed, eta)
  227.  
  228.     def _on_message(self, enum, details):
  229.         """Callback for Message signal"""
  230.         msg = AptMessage(enum, details)
  231.         self._messages.append(msg)
  232.  
  233.     def _on_error(self, code, details):
  234.         """Callback for ErrorCode signal"""
  235.         #FIXME: Store an exception instance
  236.         self._error_code = code
  237.         self._error_details = details
  238.         self.emit("error", code, details)
  239.  
  240.     def _on_finished(self, enum):
  241.         """Callback for Finished signal"""
  242.         self._exit = enum
  243.         self.emit("finished", enum)
  244.         self._owner_watcher.cancel()
  245.         if self._debconf_helper:
  246.             self._debconf_helper.stop()
  247.         self.detach()
  248.         self._main_loop.quit()
  249.         if self._exit_handler is not None:
  250.             self._exit_handler(self, enum)
  251.  
  252.     def set_method(self, method, *args):
  253.         """Setup the method of the DBus interface which should be handled"""
  254.         self._method = self._iface.get_dbus_method(method)
  255.         self._args = args
  256.  
  257.     def run(self, block=False, error_handler=None, reply_handler=None):
  258.         """Start processing the transaction.
  259.  
  260.         Keyword arguemnts:
  261.         block -- start the transaction by a sync call and return after it
  262.                  is done.
  263.                  This argument will be deprecated in the future.
  264.                  You should prefer an async call by specfying the
  265.                  error_handler and reply_handler.
  266.         error_handler -- callback whill will called in the case of an error
  267.         reply_handler -- callback which will called if the transaction is done
  268.         """
  269.         if error_handler and reply_handler:
  270.             self._method(*self._args, timeout=250,
  271.                          error_handler=error_handler,
  272.                          reply_handler=reply_handler)
  273.             return
  274.         # avoid blocking the user interface
  275.         context = gobject.main_context_default()
  276.         while context.pending():
  277.             context.iteration()
  278.         self._method(*self._args, timeout=250)
  279.         if self._exit_handler is None or block == True:
  280.             self._main_loop.run()
  281.             if self._exit_handler is None and self._error_code is not None:
  282.                 raise AptDaemonError(self._error_code, self._error_details)
  283.         return self._exit
  284.  
  285.     def get_status(self):
  286.         """Get the status of the transactioan."""
  287.         return self._status
  288.  
  289.     def get_exit_state(self):
  290.         """Return the finished status"""
  291.         return self._exit
  292.  
  293.     def get_error(self):
  294.         """Returns the AptDaemonError of a failed transaction."""
  295.         if self._error_code is not None:
  296.             return AptDaemonError(self._error_code, self._error_details)
  297.         else:
  298.             return None
  299.  
  300.     def cancel(self, reply_handler=None, error_handler=None):
  301.         """Cancel the running transaction."""
  302.         self._iface.Cancel(reply_handler=reply_handler,
  303.                            error_handler=error_handler)
  304.  
  305.     def set_http_proxy(self, proxy, 
  306.                        reply_handler=None, error_handler=None):
  307.         """Set the http_proxy property"""
  308.         self._proxy.Set("org.debian.apt.transaction", 
  309.                         "HttpProxy", proxy,
  310.                         dbus_interface=dbus.PROPERTIES_IFACE,
  311.                         reply_handler=reply_handler,
  312.                         error_handler=error_handler)
  313.  
  314.     def set_allow_unauthenticated(self, allow_unauthenticated, 
  315.                             reply_handler=None, error_handler=None):
  316.         """Set the allow_unauthenticated property"""
  317.         self._proxy.Set("org.debian.apt.transaction", 
  318.                         "AllowUnauthenticated", allow_unauthenticated,
  319.                         dbus_interface=dbus.PROPERTIES_IFACE,
  320.                         reply_handler=reply_handler,
  321.                         error_handler=error_handler)
  322.  
  323.     def set_debconf_frontend(self, frontend, reply_handler=None,
  324.                              error_handler=None):
  325.         """Set the debconf socket """
  326.         self._debconf_helper = debconf.DebconfProxy(frontend)
  327.         self._proxy.Set("org.debian.apt.transaction", "DebconfSocket",
  328.                         self._debconf_helper.socket_path,
  329.                         dbus_interface=dbus.PROPERTIES_IFACE,
  330.                         reply_handler=reply_handler,
  331.                         error_handler=error_handler)
  332.         self._debconf_helper.start()
  333.  
  334.     def set_terminal(self, ttyname, reply_handler=None, error_handler=None):
  335.         """Set the controlling terminal."""
  336.         self._proxy.Set("org.debian.apt.transaction", "Terminal", ttyname,
  337.                         dbus_interface=dbus.PROPERTIES_IFACE,
  338.                         reply_handler=reply_handler,
  339.                         error_handler=error_handler)
  340.  
  341.     def attach(self, reply_handler=None, error_handler=None):
  342.         """Request the current state from the transaction."""
  343.         self._iface.Attach(reply_handler=reply_handler,
  344.                            error_handler=error_handler)
  345.  
  346.     def detach(self):
  347.         """Stop monitoring the progress of the transaction."""
  348.         for sig in self._signal_matchers:
  349.             sig.remove()
  350.             del sig
  351.  
  352.     def set_locale(self, locale, reply_handler=None, error_handler=None):
  353.         """Set the language."""
  354.         self._proxy.Set("org.debian.apt.transaction", "Locale", locale,
  355.                         dbus_interface=dbus.PROPERTIES_IFACE,
  356.                         reply_handler=reply_handler,
  357.                         error_handler=error_handler)
  358.  
  359.     def provide_medium(self, medium, reply_handler=None, error_handler=None):
  360.         """Continue a paused transaction which waited for a medium to install
  361.         from.
  362.         """
  363.         self._iface.ProvideMedium(medium, reply_handler=reply_handler,
  364.                                   error_handler=error_handler)
  365.  
  366.     def config_file_prompt_answer(self, config, answer, reply_handler=None, 
  367.                                   error_handler=None):
  368.         """Continue a paused transaction that waits for config file prompt
  369.         """
  370.         self._iface.ConfigFilePromptAnswer(config, answer, 
  371.                                            reply_handler=reply_handler,
  372.                                            error_handler=error_handler)
  373.  
  374. class AptClient:
  375.     """Aptdaemon client wrapper class"""
  376.     def __init__(self):
  377.         """
  378.         Initialize the client.
  379.         """
  380.         self.apt_control = None
  381.         self.bus = dbus.SystemBus()
  382.         self._locale = "%s.%s" % locale.getdefaultlocale()
  383.         self.terminal = None
  384.  
  385.     def get_trusted_vendor_keys(self, exit_handler=None):
  386.         """Return the list of trusted vendors."""
  387.         daemon = get_aptdaemon()
  388.         #FIXME: Should be async
  389.         keys = daemon.GetTrustedVendorKeys()
  390.         return keys
  391.  
  392.     def upgrade_system(self, safe_mode=True, exit_handler=None):
  393.         """Upgrade system."""
  394.         return self._run_transaction("UpgradeSystem", [safe_mode],
  395.                                      exit_handler)
  396.  
  397.     def install_packages(self, package_names, exit_handler=None):
  398.         """Install packages by name."""
  399.         return self._run_transaction("InstallPackages", [package_names],
  400.                                      exit_handler)
  401.  
  402.     def add_repository(self, type, uri, dist, comps=[""], comment="", 
  403.                        sourcesfile=""):
  404.         """Add repository to the sources list.
  405.  
  406.         Keyword arguments:
  407.         type -- the type of the entry (deb, deb-src)
  408.         uri -- the main repository uri (e.g. http://archive.ubuntu.com/ubuntu)
  409.         dist -- the distribution to use (e.g. karmic, "/")
  410.         comps -- a (possible empty) list of components (main, restricted)
  411.         comment -- an (optional) comment
  412.         sourcesfile -- an (optinal) filename in sources.list.d 
  413.         """
  414.         daemon = get_aptdaemon()
  415.         # dbus can not deal with empty lists and will error
  416.         if comps == []:
  417.             comps = [""]
  418.         #FIXME: Should support async call
  419.         return daemon.AddRepository(type, uri, dist, comps, comment,
  420.                                     sourcesfile)
  421.  
  422.     def add_vendor_key_from_file(self, path, exit_handler=None):
  423.         """Add signing key from the given file to the trusted vendors."""
  424.         return self._run_transaction("AddVendorKeyFromFile", [path],
  425.                                      exit_handler)
  426.  
  427.     def remove_vendor_key(self, fingerprint, exit_handler=None):
  428.         """Remove key from the list of trusted vendors."""
  429.         return self._run_transaction("RemoveVendorKey", [fingerprint],
  430.                                      exit_handler)
  431.  
  432.     def install_file(self, path, exit_handler=None):
  433.         """Install local package file."""
  434.         return self._run_transaction("InstallFile", [path], exit_handler)
  435.  
  436.     def upgrade_packages(self, package_names, exit_handler=None):
  437.         """Upgrade packages by name."""
  438.         return self._run_transaction("UpgradePackages", [package_names],
  439.                                      exit_handler)
  440.  
  441.     def remove_packages(self, package_names, exit_handler=None):
  442.         """Remove packages by name."""
  443.         return self._run_transaction("RemovePackages", [package_names],
  444.                                      exit_handler)
  445.  
  446.     def commit_packages(self, install, reinstall, remove, purge, upgrade,
  447.                         exit_handler=None):
  448.         """Perform complex packages changes."""
  449.         return self._run_transaction("CommitPackages",
  450.                                      [install, reinstall, remove, purge,
  451.                                       upgrade],
  452.                                      exit_handler)
  453.  
  454.     def update_cache(self, exit_handler=None):
  455.         """Update repository information."""
  456.         return self._run_transaction("UpdateCache", [], exit_handler)
  457.  
  458.     def _run_transaction(self, method_name, args, exit_handler):
  459.         """Run the given method in a new transaction."""
  460.         try:
  461.             tid = self.apt_control.RequestTransaction()
  462.         except (AttributeError, dbus.DBusException), e:
  463.             if self.apt_control == None or (hasattr(e, "_dbus_error_name") and \
  464.                 e._dbus_error_name == "org.freedesktop.DBus.Error." \
  465.                                       "ServiceUnknown"):
  466.                 # first initialization (lazy) or timeout
  467.                 self.apt_control = dbus.Interface(
  468.                         self.bus.get_object("org.debian.apt", "/org/debian/apt",
  469.                                             False),
  470.                         "org.debian.apt")
  471.                 tid = self.apt_control.RequestTransaction()
  472.             else:
  473.                 raise
  474.         trans = AptTransaction(tid, self.bus)
  475.         if self._locale is not None:
  476.             trans.set_locale(self._locale)
  477.         if self.terminal is not None:
  478.             trans.set_terminal(self.terminal)
  479.         trans.set_method(method_name, *args)
  480.         if exit_handler is not None:
  481.             trans._exit_handler = exit_handler
  482.             return trans
  483.         else:
  484.             return trans.run()
  485.  
  486. def get_transaction(tid, bus=None):
  487.     """Return an AptTransaction instance connected to the given transaction.
  488.  
  489.     Keyword arguments:
  490.     tid -- the identifier of the transaction
  491.     bus -- the D-Bus connection that should be used (defaults to the system bus)
  492.     """
  493.     if not bus:
  494.         bus = dbus.SystemBus()
  495.     trans = AptTransaction(tid, bus)
  496.     trans.attach()
  497.     return trans
  498.  
  499. def get_aptdaemon(bus=None):
  500.     """Return the aptdaemon D-Bus interface.
  501.  
  502.     Keyword argument:
  503.     bus -- the D-Bus connection that should be used (defaults to the system bus)
  504.     """
  505.     if not bus:
  506.         bus = dbus.SystemBus()
  507.     return dbus.Interface(bus.get_object("org.debian.apt",
  508.                                          "/org/debian/apt",
  509.                                          False),
  510.                           "org.debian.apt")
  511.  
  512. # vim:ts=4:sw=4:et
  513.